﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Smart.Processing
{
	public abstract class ProcessingEvent
	{
		/// <summary>
		/// The higher the priority the lower a value. This is 50.
		/// </summary>
		public const int HighPriority = 50;
		/// <summary>
		/// Base priority. This is 100.
		/// </summary>
		public const int BasePriority = 100;
		/// <summary>
		/// The lower the priority the higher a value. This is 200.
		/// </summary>
		public const int LowPriority = 200;
	}

	public class ProcessingEvent<TSender, TEventArgs, TResult> : ProcessingEvent
		where TEventArgs : ProcessingEventArgs<TResult>
	{
		protected struct ProcessingEventMethod
		{
			public int Priority;
			public Action<TSender, TEventArgs> Method;
		}

		protected List<ProcessingEventMethod> Methods;

		public void AddHandler(int priority, Action<TSender, TEventArgs> handler)
		{
			if (Methods == null) Methods = new List<ProcessingEventMethod>();
			var index = Methods.FindIndex(item => item.Priority > priority);
			if (index >= 0)
				Methods.Insert(index, new ProcessingEventMethod { Priority = priority, Method = handler });
			else
				Methods.Add(new ProcessingEventMethod { Priority = priority, Method = handler });
		}

		public void RemoveHandler(Action<TSender, TEventArgs> handler)
		{
			if (Methods != null)
			{
				var index = Methods.FindLastIndex(item => item.Method == handler);
				if (index >= 0)
				{
					Methods.RemoveAt(index);
					if (Methods.Count <= 0)
					{
						Methods = null;
					}
				}
			}
		}

		public event Action<TSender, TEventArgs> Processing
		{
			add { AddHandler(BasePriority, value); }
			remove { RemoveHandler(value); }
		}

		public TResult Process(TSender sender, TEventArgs args, out bool processed)
		{
			var res = args.Result;
			processed = false;

			if (Methods == null)
				return res;

			args.StopProcessing = false;

			// ReSharper disable ForCanBeConvertedToForeach
			for (int i = 0; (i < Methods.Count) && (!args.StopProcessing); i++)
			{
				args.Processed = false;

				var handler = Methods[i];

				handler.Method(sender, args);

				if (args.Processed)
				{
					res = args.Result;
					processed = true;
				}
			}
			// ReSharper restore ForCanBeConvertedToForeach

			return res;
		}

		public TResult Process(TSender sender, TEventArgs args)
		{
			bool processed;
			return Process(sender, args, out processed);
		}
	}
}
